package com.iwdnb.gkgz.common.utils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import cn.hutool.core.date.DateUtil;
import com.google.common.collect.Lists;
import com.iwdnb.bmnf.common.utils.BeanConvertUtils;
import com.iwdnb.bmnf.common.utils.BooleanUtils;
import com.iwdnb.gkgz.common.model.dto.StockDayData;
import com.iwdnb.gkgz.common.model.dto.StockLimitUpData;
import com.iwdnb.gkgz.common.model.dto.StockSupportDTO;
import com.iwdnb.gkgz.common.model.dto.StockSupportDTO.StockSupportCheckInfo;
import com.iwdnb.gkgz.common.model.dto.StockSupportDTO.StockSupportItem;
import com.iwdnb.gkgz.common.quota.MovingAverage;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import static com.iwdnb.gkgz.common.utils.BigDecimalUtils.onePointZeroOneOne;
import static com.iwdnb.gkgz.common.utils.StockUtils.getAveragePrice;

@Slf4j
@Component
public class TulongdaoUtilsNew {
    private static final int fivePeriod = 5;
    public static final int sevenPeriod = 7;
    private static final int tenPeriod = 10;
    private static final int thirteenPeriod = 13;
    private static final int twentyPeriod = 20;
    private static final int thirtyPeriod = 30;
    private static final int sixtyPeriod = 60;
    public static final List<Integer> ALL_PERIODS = Lists.newArrayList(twentyPeriod, thirteenPeriod, tenPeriod,
        sixtyPeriod, thirtyPeriod);
    private static final Map<Integer, String> PERIOD_MAP = new LinkedHashMap<>();

    public static final BigDecimal ten = new BigDecimal(10);
    public static final BigDecimal ninePointSix = new BigDecimal(9.6);
    public static final BigDecimal seven = new BigDecimal(7);
    public static final BigDecimal sixPointFive = new BigDecimal(6.5);
    public static final BigDecimal five = new BigDecimal(5);
    public static final BigDecimal three = new BigDecimal(3);

    public static final BigDecimal twoPointOne = new BigDecimal(2.1);
    public static final BigDecimal two = new BigDecimal(2);
    public static final BigDecimal onePointOne = new BigDecimal(1.1);
    public static final BigDecimal one = new BigDecimal(1);
    public static final BigDecimal zero = new BigDecimal(0);
    public static final BigDecimal zeroPointFive = new BigDecimal(0.5);

    /**
     * 预测明日股票屠龙刀支撑价格
     *
     * @param code
     * @param stockDayDataList
     * @return
     */
    public static StockSupportDTO predictTldSupportPrice(String code, List<StockDayData> stockDayDataList) {
        StockSupportDTO stockSupportDTO = new StockSupportDTO();
        stockSupportDTO.setCode(code);
        StockDayData latestData = BeanConvertUtils.convert(stockDayDataList.get(stockDayDataList.size() - 1),
            StockDayData.class);
        stockDayDataList = BeanConvertUtils.convertList(stockDayDataList, StockDayData.class);
        stockDayDataList.add(latestData);
        BigDecimal closePrice = latestData.getClosePrice();
        BigDecimal limitUpPrice = BigDecimalUtils.multiply(latestData.getClosePrice(), BigDecimalUtils.onePointOne);
        BigDecimal limitDownPrice = BigDecimalUtils.multiply(latestData.getClosePrice(), BigDecimalUtils.zeroPointNine);
        latestData.setOpenPrice(limitUpPrice);
        latestData.setClosePrice(limitUpPrice);
        latestData.setMaxPrice(limitUpPrice);
        latestData.setMinPrice(limitUpPrice);
        MovingAverage.calculateEMA(stockDayDataList, ALL_PERIODS);
        List<StockSupportItem> supportItems = new ArrayList<>();
        for (Integer period : ALL_PERIODS) {
            BigDecimal avg = getFixedAveragePrice(latestData, period);
            //均线在涨停板之上，跳过
            if (BigDecimalUtils.isGt(avg, limitUpPrice)) {
                log.error("{}-{} avg isGreatThan limitUpPrice,ignore,avg:{},limitUpPrice:{}", code, period, avg,
                    limitUpPrice);
                continue;
            }
            //计算修正后的均线
            BigDecimal fixPrice = calculateAvgStockPrice(limitUpPrice, avg, period);
            BigDecimal fixAvg = getFixedAveragePrice(avg, limitUpPrice, fixPrice, period);
            if (BigDecimalUtils.isGe(fixPrice, limitDownPrice)) {
                log.info("{}-{} avg isMatch price:{},avg:{},fixAvg:{}", code, period, fixPrice, avg, fixAvg
                );
                StockSupportItem supportItem = new StockSupportItem();
                supportItem.setPeriod(period);
                supportItem.setPrice(fixAvg);
                supportItem.setBuyPrice(BigDecimalUtils.multiply(fixAvg, new BigDecimal(1.011)));

                supportItems.add(supportItem);
            }
        }
        if (CollectionUtils.isEmpty(supportItems)) {
            return null;
        }
        Collections.sort(supportItems, (o1, o2) -> o2.getPrice().compareTo(o1.getPrice()));
        //只有一条均线符合要求,就直接返回
        if (supportItems.size() == 1) {
            stockSupportDTO.setFirst(supportItems.get(0));
            return stockSupportDTO;
        }

        BigDecimal firstPrice = supportItems.get(0).getPrice();
        stockSupportDTO.setFirst(supportItems.get(0));
        List<StockSupportItem> firstItems = getMatchSupportItem(supportItems, firstPrice);
        if (CollectionUtils.isNotEmpty(firstItems)) {
            List<Integer> periods = firstItems.stream().map(StockSupportItem::getPeriod).collect(Collectors.toList());
            stockSupportDTO.getFirst().setJoinPeriod(StringUtils.join(periods, ","));
        }
        supportItems = supportItems.subList(firstItems.size(), supportItems.size());
        //第二支撑位
        if (CollectionUtils.isNotEmpty(supportItems)) {
            BigDecimal secondPrice = supportItems.get(0).getPrice();
            stockSupportDTO.setSecond(supportItems.get(0));
            List<StockSupportItem> secondItems = getMatchSupportItem(supportItems, secondPrice);
            if (CollectionUtils.isNotEmpty(secondItems)) {
                List<Integer> periods = secondItems.stream().map(StockSupportItem::getPeriod).collect(
                    Collectors.toList());
                stockSupportDTO.getSecond().setJoinPeriod(StringUtils.join(periods, ","));
            }
            supportItems = supportItems.subList(secondItems.size(), supportItems.size());
        }
        //第三支撑位
        if (CollectionUtils.isNotEmpty(supportItems)) {
            BigDecimal thirdPrice = supportItems.get(0).getPrice();
            stockSupportDTO.setThird(supportItems.get(0));
            List<StockSupportItem> thirdItems = getMatchSupportItem(supportItems, thirdPrice);
            if (CollectionUtils.isNotEmpty(thirdItems)) {
                List<Integer> periods = thirdItems.stream().map(StockSupportItem::getPeriod).collect(
                    Collectors.toList());
                stockSupportDTO.getThird().setJoinPeriod(StringUtils.join(periods, ","));
            }
        }
        return stockSupportDTO;
    }

    public static void checkPreditctionOld(StockSupportDTO stockSupport, StockDayData data, StockDayData afterData) {
        String remark = null;
        String matchFlag = null;
        BigDecimal compareRate = BigDecimalUtils.three;
        BigDecimal closePrice = data.getClosePrice();
        BigDecimal price = BigDecimalUtils.isGe(closePrice, afterData.getOpenPrice())
            ? afterData.getOpenPrice() : closePrice;
        if (Objects.nonNull(afterData)) {
            stockSupport.setRealtimePrice(afterData.getClosePrice());
            stockSupport.setOpenPrice(afterData.getOpenPrice());
            stockSupport.setMinPrice(afterData.getMinPrice());
        }
        BigDecimal supportPrice = null;
        Integer supportLevel = null;
        BigDecimal firstSupportPrice = stockSupport.getFirst().getPrice();
        //第一支撑位比昨日收盘价低3个点以上，第一支撑位有效
        BigDecimal rate = BigDecimalUtils.subStractAndDividePrecent(price, firstSupportPrice);
        if (BigDecimalUtils.isGe(rate, compareRate)) {
            BigDecimal supportRate = BigDecimalUtils.subStractAndDividePrecent(closePrice, firstSupportPrice);
            BigDecimal minRate = BigDecimalUtils.subStractAndDividePrecent(firstSupportPrice,
                afterData.getMinPrice());
            if (BigDecimalUtils.isGe(supportRate, BigDecimalUtils.eight)) {
                matchFlag = BooleanUtils.FALSE;
                remark = "第一支撑跌幅过大,与开盘价相差" + supportRate + "%";
            } else if (BigDecimalUtils.isGe(minRate, BigDecimalUtils.fuOnePointOne)) {
                matchFlag = BooleanUtils.TRUE;
                supportLevel = 1;
                supportPrice = firstSupportPrice;
                remark = "第一支撑有效,与最低价相差" + minRate + "%";
            } else {
                matchFlag = BooleanUtils.FALSE;
                remark = "第一支撑有效,但未到支撑,与最低价相差" + minRate + "%";
            }
        } else if (Objects.nonNull(stockSupport.getSecond())) {
            //验证第二支撑
            BigDecimal secondSupportPrice = stockSupport.getSecond().getPrice();
            BigDecimal supportRate = BigDecimalUtils.subStractAndDividePrecent(closePrice, secondSupportPrice);
            rate = BigDecimalUtils.subStractAndDividePrecent(price, secondSupportPrice);
            //第二支撑跌幅过大跌幅过大
            if (BigDecimalUtils.isGe(supportRate, BigDecimalUtils.eight)) {
                matchFlag = BooleanUtils.FALSE;
                remark = "第二支撑跌幅过大,与开盘价相差" + supportRate + "%";
            } else if (BigDecimalUtils.isLt(afterData.getOpenPrice(), secondSupportPrice)) {
                //开盘价跌破第二支撑
                remark = "开盘价跌破第二支撑";
                matchFlag = BooleanUtils.FALSE;
            } else if (BigDecimalUtils.isGe(rate, compareRate)) {
                //与基准价格差额到达3个点以上，第二支撑有效
                BigDecimal minRate = BigDecimalUtils.subStractAndDividePrecent(secondSupportPrice,
                    afterData.getMinPrice());
                //股价到达第二支撑
                if (BigDecimalUtils.isGe(minRate, BigDecimalUtils.fuOnePointOne)) {
                    matchFlag = BooleanUtils.TRUE;
                    supportLevel = 2;
                    supportPrice = secondSupportPrice;
                    remark = "第二支撑有效,与最低价相差" + minRate + "%";
                } else {
                    //股价未到达第二支撑
                    matchFlag = BooleanUtils.FALSE;
                    remark = "第二支撑有效,但未到支撑,与最低价相差" + minRate + "%";
                }

            } else {
                remark = "第二支撑无效";
                matchFlag = BooleanUtils.FALSE;
            }
        } else {
            remark = "无第二支撑";
            matchFlag = BooleanUtils.FALSE;
        }
        stockSupport.setRemark(remark);
        stockSupport.setMatchFlag(matchFlag);
        stockSupport.setSupportLevel(supportLevel);
        if (Objects.nonNull(supportPrice)) {
            supportPrice = BigDecimalUtils.multiply(supportPrice, onePointZeroOneOne);
            stockSupport.setSupportPrice(supportPrice);
            stockSupport.setRealtimeRate(
                BigDecimalUtils.subStractAndDividePrecent(afterData.getClosePrice(), supportPrice));
        }
    }

    public static void checkPreditction(StockSupportDTO stockSupport, StockDayData data, StockDayData afterData,
        int markEmotion, int currentEmotion) {
        stockSupport.setDate(afterData.getDate());
        if (Objects.nonNull(afterData)) {
            stockSupport.setRealtimePrice(afterData.getClosePrice());
            stockSupport.setOpenPrice(afterData.getOpenPrice());
            stockSupport.setMinPrice(afterData.getMinPrice());
        }
        StockSupportCheckInfo checkInfo = checkSupport(stockSupport.getFirst(), 1, data, afterData, markEmotion,
            currentEmotion);
        if (BooleanUtils.isTrue(checkInfo.getMatchFlag())) {
            setMatchCheckInfo(stockSupport, checkInfo, afterData, markEmotion, currentEmotion);
            return;
        }
        if (Objects.nonNull(stockSupport.getSecond())) {
            checkInfo = checkSupport(stockSupport.getSecond(), 2, data, afterData, markEmotion, currentEmotion);
            if (BooleanUtils.isTrue(checkInfo.getMatchFlag())) {
                setMatchCheckInfo(stockSupport, checkInfo, afterData, markEmotion, currentEmotion);
                return;
            }
        }
        if (Objects.nonNull(stockSupport.getThird())) {
            checkInfo = checkSupport(stockSupport.getThird(), 3, data, afterData, markEmotion, currentEmotion);
            if (BooleanUtils.isTrue(checkInfo.getMatchFlag())) {
                setMatchCheckInfo(stockSupport, checkInfo, afterData, markEmotion, currentEmotion);
                return;
            }
        }
        //设置未匹配支撑位的信息
        stockSupport.setRemark(checkInfo.getRemark());
        stockSupport.setMatchFlag(checkInfo.getMatchFlag());

    }

    /**
     * 是否涨停次日带上影线且上涨
     *
     * @param stockDayDataList
     * @return
     */
    public static boolean isSignalAfterOneDateUpLineUp(List<StockDayData> stockDayDataList, String limitUpDate) {
        String afterOneDate = DateUtil.formatDate(WorkDayUtils.getNextWorkDay(DateUtil.parseDate(limitUpDate)));
        StockDayData limitUpdata = StockUtils.getStockDayData(stockDayDataList, limitUpDate);
        StockDayData afterOneData = StockUtils.getStockDayData(stockDayDataList, afterOneDate);
        if (Objects.isNull(limitUpdata) || Objects.isNull(afterOneData)) {
            return false;
        }
        BigDecimal upPrice = BigDecimalUtils.isGe(afterOneData.getClosePrice(), afterOneData.getOpenPrice())
            ? afterOneData.getClosePrice() : afterOneData.getOpenPrice();
        BigDecimal upLineRate = BigDecimalUtils.subStractAndDividePrecent(afterOneData.getMaxPrice(), upPrice);
        BigDecimal afterRate = BigDecimalUtils.subStractAndDividePrecent(afterOneData.getClosePrice(),
            limitUpdata.getClosePrice());
        if (BigDecimalUtils.isGe(upLineRate, BigDecimalUtils.one)
            && BigDecimalUtils.isGe(afterRate, BigDecimalUtils.zeroPointFive)) {
            return true;
        }
        return false;
    }

    private static void setMatchCheckInfo(StockSupportDTO stockSupport, StockSupportCheckInfo checkInfo,
        StockDayData afterData, int markEmotion, int currentEmotion) {
        stockSupport.setSupportLevel(checkInfo.getSupportLevel());
        stockSupport.setRemark(checkInfo.getRemark());
        stockSupport.setMatchFlag(checkInfo.getMatchFlag());
        BigDecimal supportPrice = checkInfo.getSupportPrice();
        supportPrice = BigDecimalUtils.multiply(supportPrice, BigDecimalUtils.onePointZeroOneOne);
        stockSupport.setSupportPrice(supportPrice);
        stockSupport.setRealtimeRate(
            BigDecimalUtils.subStractAndDividePrecent(afterData.getClosePrice(), supportPrice));
    }

    private static StockSupportCheckInfo checkSupport(StockSupportItem supportItem,
        int index, StockDayData data,
        StockDayData afterData, int markEmotion, int currentEmotion) {
        BigDecimal supportPrice = supportItem.getPrice();
        BigDecimal closePrice = data.getClosePrice();
        BigDecimal basicPrice = BigDecimalUtils.divide(closePrice.add(afterData.getOpenPrice()), BigDecimalUtils.two);
        BigDecimal supportRate = BigDecimalUtils.subStractAndDividePrecent(closePrice, supportPrice);
        BigDecimal minRate = BigDecimalUtils.subStractAndDividePrecent(supportPrice,
            afterData.getMinPrice());
        //支撑位比昨日收盘价低3个点以上，支撑位有效
        BigDecimal rate = BigDecimalUtils.subStractAndDividePrecent(basicPrice, supportPrice);
        StockSupportCheckInfo checkInfo = new StockSupportCheckInfo();
        BigDecimal compareRate = getCompareRate(markEmotion, currentEmotion);
        BigDecimal supportCompareRate = getSupportCompareRate(markEmotion, currentEmotion);
        BigDecimal supportPriceRate = getSupportPriceRate(markEmotion, currentEmotion);
        if (BigDecimalUtils.isGe(supportRate, BigDecimalUtils.eightPointEight)) {
            checkInfo.setMatchFlag(BooleanUtils.FALSE);
            checkInfo.setRemark("第" + index + "支撑跌幅过大,与开盘价相差" + supportRate + "%");
        } else if (BigDecimalUtils.isLt(afterData.getOpenPrice(), supportPrice)) {
            //开盘价跌破支撑
            checkInfo.setMatchFlag(BooleanUtils.FALSE);
            checkInfo.setRemark("开盘价跌破第" + index + "支撑");
        } else if (BigDecimalUtils.isGe(rate, compareRate)) {
            if (BigDecimalUtils.isGe(minRate, supportCompareRate)) {
                checkInfo.setMatchFlag(BooleanUtils.TRUE);
                checkInfo.setSupportLevel(index);
                checkInfo.setSupportPrice(supportPrice);
                checkInfo.setRemark("第" + index + "支撑有效,与最低价相差" + minRate + "%");
            } else {
                checkInfo.setMatchFlag(BooleanUtils.FALSE);
                checkInfo.setRemark("第" + index + "支撑有效,但未到支撑,与最低价相差" + minRate + "%");
            }
        } else {
            checkInfo.setMatchFlag(BooleanUtils.FALSE);
            checkInfo.setRemark("第" + index + "支撑无效");
        }
        return checkInfo;
    }

    private static BigDecimal getCompareRate(int markEmotion, int currentEmotion) {
        if (EmotionAnalysis.isActiveEmotion(markEmotion) && EmotionAnalysis.isActiveEmotion(currentEmotion)) {
            Integer rate = 10 - currentEmotion;
            return BigDecimalUtils.of(rate);
        }
        return BigDecimalUtils.three;
    }

    private static BigDecimal getSupportCompareRate(int markEmotion, int currentEmotion) {
        if (EmotionAnalysis.isColdEmotion(markEmotion)) {
            if (EmotionAnalysis.isNegativeEmotion(currentEmotion)) {
                double rate = 1.9 - currentEmotion;
                return BigDecimalUtils.of(rate);
            }
        }
        if (EmotionAnalysis.isSensitiveEmotion(currentEmotion)) {
            return BigDecimalUtils.zeroPointFive;
        }
        return BigDecimalUtils.fuOnePointOne;
    }

    private static BigDecimal getSupportPriceRate(int markEmotion, int currentEmotion) {
        if (EmotionAnalysis.isColdEmotion(markEmotion)) {
            if (EmotionAnalysis.isNegativeEmotion(currentEmotion)) {
                double rate = 100 + currentEmotion - 1.9;
                return BigDecimalUtils.of(rate / 100);
            }
        }
        return BigDecimalUtils.onePointZeroOneOne;
    }

    private static List<StockSupportItem> getMatchSupportItem(List<StockSupportItem> supportItems, BigDecimal price) {
        List<StockSupportItem> items = new ArrayList<>();
        for (int i = 0; i < supportItems.size(); i++) {
            StockSupportItem item = supportItems.get(i);
            BigDecimal rate = BigDecimalUtils.subStractAndDividePrecent(price, item.getPrice());
            if (BigDecimalUtils.isLe(rate, BigDecimalUtils.two)) {
                items.add(item);
                continue;
            }
            break;
        }
        return items;
    }

    private static BigDecimal calculateAvgStockPrice(BigDecimal limitUpPrice, BigDecimal avg, Integer period) {
        //计算差价平均,与均线相减
        BigDecimal priceRange = BigDecimalUtils.subtract(limitUpPrice, avg);
        priceRange = BigDecimalUtils.divide(priceRange, new BigDecimal(period));
        return BigDecimalUtils.subtract(avg, priceRange);
    }

    private static BigDecimal getFixedAveragePrice(StockDayData data, Integer period) {
        BigDecimal avg = getAveragePrice(data, period);
        //计算差价平均,与均线相减
        BigDecimal priceRange = BigDecimalUtils.subtract(data.getClosePrice(), data.getMinPrice());
        BigDecimal avgRange = BigDecimalUtils.divide(priceRange, new BigDecimal(period));
        return BigDecimalUtils.subtract(avg, avgRange);
    }

    private static BigDecimal getFixedAveragePrice(BigDecimal avg, BigDecimal currentPrice, BigDecimal minPrice,
        Integer period) {
        //计算差价平均,与均线相减
        BigDecimal priceRange = BigDecimalUtils.subtract(currentPrice, minPrice);
        BigDecimal avgRange = BigDecimalUtils.divide(priceRange, new BigDecimal(period));
        return BigDecimalUtils.subtract(avg, avgRange);
    }

}
